# -*- coding: utf-8 -*-
"""
文件操作工具类
提供通用的文件操作方法，避免重复代码
"""

import os
import re
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Tuple
from gglobal import logger


class FileUtils:
    """文件操作工具类"""
    
    @staticmethod
    def validate_file_path(path: str) -> bool:
        """验证文件路径是否有效"""
        if not path or not isinstance(path, str):
            return False
        
        # 检查路径长度
        if len(path) > 4096:  # 大多数系统的路径长度限制
            return False
            
        # 检查非法字符（Windows）
        illegal_chars = r'[<>:"|?*]'
        if re.search(illegal_chars, path):
            return False
            
        return True
    
    @staticmethod
    def normalize_path(path: str) -> str:
        """标准化路径格式"""
        if not path:
            return "/"
            
        # 统一使用正斜杠
        normalized = path.replace('\\', '/')
        
        # 确保以/开头
        if not normalized.startswith('/'):
            normalized = '/' + normalized
            
        # 移除末尾的/（除非是根目录）
        if len(normalized) > 1 and normalized.endswith('/'):
            normalized = normalized.rstrip('/')
            
        return normalized
    
    @staticmethod
    def get_parent_path(path: str) -> str:
        """获取父目录路径"""
        normalized = FileUtils.normalize_path(path)
        if normalized == "/":
            return "/"
            
        parent = os.path.dirname(normalized)
        return FileUtils.normalize_path(parent)
    
    @staticmethod
    def join_path(base_path: str, *paths: str) -> str:
        """连接路径"""
        result = FileUtils.normalize_path(base_path)
        
        for path in paths:
            if not path:
                continue
                
            # 移除开头的/
            clean_path = path.lstrip('/')
            if clean_path:
                result = result.rstrip('/') + '/' + clean_path
                
        return FileUtils.normalize_path(result)
    
    @staticmethod
    def get_file_extension(filename: str) -> str:
        """获取文件扩展名"""
        if not filename or '.' not in filename:
            return ""
            
        return os.path.splitext(filename)[1].lower()
    
    @staticmethod
    def is_archive_file(filename: str) -> bool:
        """判断是否为压缩文件"""
        archive_extensions = {
            '.zip', '.rar', '.7z', '.tar', '.gz', '.bz2', 
            '.xz', '.tar.gz', '.tar.bz2', '.tar.xz'
        }
        
        ext = FileUtils.get_file_extension(filename)
        if ext in archive_extensions:
            return True
            
        # 检查双扩展名（如.tar.gz）
        if '.' in filename:
            parts = filename.split('.')
            if len(parts) >= 3:
                double_ext = '.' + '.'.join(parts[-2:])
                return double_ext.lower() in archive_extensions
                
        return False
    
    @staticmethod
    def is_image_file(filename: str) -> bool:
        """判断是否为图片文件"""
        image_extensions = {
            '.jpg', '.jpeg', '.png', '.gif', '.bmp', 
            '.tiff', '.tif', '.webp', '.svg', '.ico'
        }
        
        ext = FileUtils.get_file_extension(filename)
        return ext in image_extensions
    
    @staticmethod
    def is_video_file(filename: str) -> bool:
        """判断是否为视频文件"""
        video_extensions = {
            '.mp4', '.avi', '.mkv', '.mov', '.wmv', 
            '.flv', '.webm', '.m4v', '.3gp', '.ts'
        }
        
        ext = FileUtils.get_file_extension(filename)
        return ext in video_extensions
    
    @staticmethod
    def is_text_file(filename: str) -> bool:
        """判断是否为文本文件"""
        text_extensions = {
            '.txt', '.log', '.md', '.rst', '.json', '.xml', 
            '.yaml', '.yml', '.ini', '.cfg', '.conf', '.py', 
            '.js', '.html', '.htm', '.css', '.sql', '.sh', '.bat'
        }
        
        ext = FileUtils.get_file_extension(filename)
        return ext in text_extensions
    
    @staticmethod
    def format_file_size(size_bytes: int) -> str:
        """格式化文件大小显示"""
        if size_bytes == 0:
            return "0 B"
            
        size_names = ["B", "KB", "MB", "GB", "TB"]
        i = 0
        size = float(size_bytes)
        
        while size >= 1024.0 and i < len(size_names) - 1:
            size /= 1024.0
            i += 1
            
        return f"{size:.1f} {size_names[i]}"
    
    @staticmethod
    def sanitize_filename(filename: str) -> str:
        """清理文件名，移除非法字符"""
        if not filename:
            return "untitled"
            
        # 移除或替换非法字符
        illegal_chars = r'[<>:"|?*\\]'
        sanitized = re.sub(illegal_chars, '_', filename)
        
        # 移除开头和结尾的空格和点
        sanitized = sanitized.strip(' .')
        
        # 确保不为空
        if not sanitized:
            sanitized = "untitled"
            
        # 限制长度
        if len(sanitized) > 255:
            name, ext = os.path.splitext(sanitized)
            max_name_len = 255 - len(ext)
            sanitized = name[:max_name_len] + ext
            
        return sanitized
    
    @staticmethod
    def create_backup_filename(original_path: str) -> str:
        """创建备份文件名"""
        path = Path(original_path)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        if path.suffix:
            # 有扩展名的文件
            backup_name = f"{path.stem}_backup_{timestamp}{path.suffix}"
        else:
            # 无扩展名的文件
            backup_name = f"{path.name}_backup_{timestamp}"
            
        return str(path.parent / backup_name)
    
    @staticmethod
    def split_path_components(path: str) -> List[str]:
        """分割路径为组件列表"""
        normalized = FileUtils.normalize_path(path)
        if normalized == "/":
            return ["/"]
            
        components = []
        parts = normalized.strip('/').split('/')
        
        # 添加根目录
        components.append("/")
        
        # 添加各级目录
        current_path = ""
        for part in parts:
            if part:
                current_path += "/" + part
                components.append(current_path)
                
        return components
    
    @staticmethod
    def get_relative_path(base_path: str, target_path: str) -> str:
        """获取相对路径"""
        base_normalized = FileUtils.normalize_path(base_path)
        target_normalized = FileUtils.normalize_path(target_path)
        
        if target_normalized.startswith(base_normalized):
            relative = target_normalized[len(base_normalized):]
            return relative.lstrip('/')
        else:
            return target_normalized


class PathValidator:
    """路径验证器"""
    
    @staticmethod
    def is_safe_path(path: str, base_path: str = "/") -> bool:
        """检查路径是否安全（防止路径遍历攻击）"""
        try:
            # 标准化路径
            normalized_path = FileUtils.normalize_path(path)
            normalized_base = FileUtils.normalize_path(base_path)
            
            # 检查是否包含路径遍历
            if ".." in normalized_path:
                return False
                
            # 检查是否在基础路径内
            if not normalized_path.startswith(normalized_base):
                return False
                
            return True
            
        except Exception as e:
            logger.error(f"路径安全检查失败: {e}")
            return False
    
    @staticmethod
    def validate_upload_path(local_path: str, remote_base: str) -> Tuple[bool, str]:
        """验证上传路径"""
        if not os.path.exists(local_path):
            return False, "本地文件不存在"
            
        if not os.path.isfile(local_path):
            return False, "路径不是文件"
            
        # 检查文件大小（限制为100MB）
        try:
            file_size = os.path.getsize(local_path)
            if file_size > 100 * 1024 * 1024:  # 100MB
                return False, "文件过大（超过100MB）"
        except OSError as e:
            return False, f"无法获取文件大小: {e}"
            
        return True, "验证通过"
    
    @staticmethod
    def validate_download_path(remote_path: str, local_dir: str) -> Tuple[bool, str]:
        """验证下载路径"""
        if not FileUtils.validate_file_path(remote_path):
            return False, "远程路径格式无效"
            
        if not os.path.exists(local_dir):
            try:
                os.makedirs(local_dir, exist_ok=True)
            except OSError as e:
                return False, f"无法创建本地目录: {e}"
                
        if not os.path.isdir(local_dir):
            return False, "本地路径不是目录"
            
        return True, "验证通过"